﻿using System;
using System.Linq;
using System.Collections.Generic;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.LinearAlgebra.Generic;
using innovations.util.exts.mathdotnet;
using innovations.ml.core.solvers;

namespace innovations.ml.core.models
{
    public class Model
    {
        public Model() { }

        public Model(Matrix<double> x, Vector<double> y, bool includesOnesColumn, int numberOfLabels = 1, double lambda = 0.0)
        {
            if (!includesOnesColumn)
                X = x.InsertColumn(0, new DenseVector(x.RowCount, 1.0));
            else
                X = x;
            Y = y;
            Predictions = new DenseVector(X.RowCount);
            Mu = new DenseVector(X.ColumnCount);
            Sigma = new DenseVector(X.ColumnCount);
            Gradient = new DenseVector(X.ColumnCount);
            Theta = new DenseVector(X.ColumnCount);
            NumberOfLabels = numberOfLabels;
            Lambda = lambda;
        }

        public void ScaleFeatures(double range)
        {
            for (int i = 0; i < X.ColumnCount; i++)
            {
                double average = X.Column(i).Average();
                for (int j = 0; j < X.RowCount; j++)
                    X[j, i] = (X[j, i] - average) / range;
            }
        }

        /// <summary>
        /// Used in multi-class classification.  
        /// </summary>
        /// <param name="label">scalar used to compare against #Y</param>
        /// <returns>Vector of 1s and 0s based on wether or not the label exists in as an element in #Y</returns>
        public void SetLogicalY(int label)
        {
            LogicalY = Y.PointwiseEqual(label);
        }

        public Matrix<double> X { get; set; }
        public Vector<double> Y { get; set; }
        public Vector<double> LogicalY { get; set; }
        public Vector<double> Theta { get; set; }
        public Matrix<double> MultiClassTheta { get; set; }
        public List<Matrix<double>> MultiLevelMultiClassTheta { get; set; }
        public Vector<double> Gradient { get; set; }
        public Vector<double> Mu { get; set; }
        public Vector<double> Sigma { get; set; }
        public double J { get; set; }
        public Vector<double> J_History { get; set; }
        public Vector<double> Predictions { get; set; }
        public double Lambda { get; set; }
        public Solver Solver { get; set; }
        public int NumberOfLabels { get; set; }
    }
}